package com.ssy.lingxi.report.serviceimpl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import com.ssy.lingxi.common.constant.Constants;
import com.ssy.lingxi.common.constant.settle.SettlementOrderTypeEnum;
import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.model.dto.UserLoginCacheDTO;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.component.redis.service.IRedisStringUtils;
import com.ssy.lingxi.message.api.constant.MessageTemplateCode;
import com.ssy.lingxi.message.api.vo.request.SystemMessageRequest;
import com.ssy.lingxi.report.api.enums.CommonBooleanEnum;
import com.ssy.lingxi.report.dao.aftersale.ReturnGoodsDAO;
import com.ssy.lingxi.report.dao.merchantmember.MemberDAO;
import com.ssy.lingxi.report.dao.order.ProcurementOrderDAO;
import com.ssy.lingxi.report.dao.platformmanage.PlatformParameterManageDAO;
import com.ssy.lingxi.report.entity.settleaccount.*;
import com.ssy.lingxi.report.model.bo.MemberBO;
import com.ssy.lingxi.report.model.bo.PlatformParameterManageBO;
import com.ssy.lingxi.report.model.bo.PlatformScoreSettlementBO;
import com.ssy.lingxi.report.model.bo.PlatformSettlementBO;
import com.ssy.lingxi.report.model.constant.settleaccount.PlatformSettlementOrderTypeEnum;
import com.ssy.lingxi.report.model.constant.settleaccount.SettlementStatusEnum;
import com.ssy.lingxi.report.model.constant.settleaccount.SettlementStrategyStatusEnum;
import com.ssy.lingxi.report.model.constant.settleaccount.SettlementWayEnum;
import com.ssy.lingxi.report.model.vo.convert.PlatformScoreSettlementDetailCVO;
import com.ssy.lingxi.report.model.vo.convert.PlatformSettlementDetailCVO;
import com.ssy.lingxi.report.model.vo.request.PlatformManualSettlementVO;
import com.ssy.lingxi.report.model.vo.request.ScoreManualSettlementVO;
import com.ssy.lingxi.report.repository.settleaccount.*;
import com.ssy.lingxi.report.service.IFeignService;
import com.ssy.lingxi.report.service.IPlatformSettlementService;
import com.ssy.lingxi.report.utils.SerialNumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.persistence.criteria.Predicate;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 平台结算接口实现
 * @author 伍将
 * @date 2020/12/2
 * @version 2.0.0
 */
@Service
public class PlatformSettlementServiceImpl implements IPlatformSettlementService {

    private static final Logger logger = LoggerFactory.getLogger(PlatformSettlementServiceImpl.class);

    @Resource
    private PlatformSettlementStrategyRepository platformSettlementStrategyRepository;

    @Resource
    private PlatformSettlementRepository platformSettlementRepository;

    @Resource
    private PlatformSettlementDetailRepository platformSettlementDetailRepository;

    @Resource
    private PlatformScoreSettlementRepository platformScoreSettlementRepository;

    @Resource
    private PlatformScoreSettlementDetailRepository platformScoreSettlementDetailRepository;

    @Resource
    private IRedisStringUtils redisStringUtils;

    @Resource
    private MemberDAO memberDAO;

    @Resource
    private ProcurementOrderDAO procurementOrderDAO;

    @Resource
    private PlatformParameterManageDAO platformParameterManageDAO;

    @Resource
    private ReturnGoodsDAO returnGoodsDAO;

    @Resource
    private IFeignService feignService;

    /**
     * 平台结算
     * @author 伍将
     * @date 2020/12/2
     * @param startDate: 结算时间
     **/
    @Override
    public void settlementReport(DateTime startDate) {
        logger.info("平台结算，日期：{}",startDate.toJdkDate());

        // 积分
        scoreSettlement(startDate);

        // 代收款
        platformSettlement(startDate);
    }

    /**
     * 积分手动结算
     * @author 伍将
     * @date 2020/12/24
     * @param user ：当前登录用户
     * @param settlementVO:结算参数
     * @return 统一数据模型
     **/
    @Transactional
    @Override
    public Wrapper<Void> scoreManualSettlement(UserLoginCacheDTO user, ScoreManualSettlementVO settlementVO) {

        PlatformScoreSettlementDO settlementDO = platformScoreSettlementRepository.findById(settlementVO.getId()).orElse(null);
        if (settlementDO == null) {
            throw new BusinessException(ResponseCode.SA_PLATFORM_SCORE_SETTLEMENT_NOT_EXIST);
        }

        List<PlatformScoreSettlementDO> settlementDOList = platformScoreSettlementRepository.findAllByIdGreaterThanEqualAndMemberIdAndRoleIdOrderBySettlementDateAsc(settlementVO.getId(), settlementDO.getMemberId(), settlementDO.getRoleId());
        if (settlementDOList == null || settlementDOList.size() <= 0) {
            throw new BusinessException(ResponseCode.SA_PLATFORM_SCORE_SETTLEMENT_NOT_EXIST);
        }
        // 如果结算数据有已对账数据，则不允许重跑结算
        if (settlementDOList.stream().anyMatch(o -> !SettlementStatusEnum.TO_BE_ACCOUNT_COMPLETE.getCode().equals(o.getStatus()))) {
            throw new BusinessException(ResponseCode.SA_SETTLEMENT_DATA_CHECK);
        }

        List<Long> idList = settlementDOList.stream().map(PlatformScoreSettlementDO::getId).collect(Collectors.toList());

        // 获取佣金比率
        PlatformParameterManageBO platformParameterManageBO = platformParameterManageDAO.getParameter(com.ssy.lingxi.report.constant.Constants.SCORE_SETTLEMENT_EXCHANGE_CODE);
        Double rate = platformParameterManageBO != null ? platformParameterManageBO.getValue() : 0;
        if (rate <= 0) {
            logger.info("平台配置有效的积分兑换比率小于等于0，无需结算");
            // 删除积分结算数据
            platformScoreSettlementRepository.deleteAllByIdIn(idList);
            // 删除积分结算明细
            platformScoreSettlementDetailRepository.deleteAllBySettlementIdIn(idList);
        } else {
            // 有积分兑换比率,进行重新结算
            settlementDOList.forEach(o -> {
                o.setRatio(rate);
                o.setScore(0D);
                o.setTotalCount(0L);
                o.setAmount(0D);
            });
            // 重新结算前，先清除旧数据
            platformScoreSettlementRepository.saveAll(settlementDOList);
            // 删除积分结算明细
            platformScoreSettlementDetailRepository.deleteAllBySettlementIdIn(idList);

            settlementDOList.forEach(o -> {
                PlatformSettlementStrategyDO strategyDO = platformSettlementStrategyRepository.findById(o.getStrategyId()).orElse(null);
                // 如果对应的结算策略已删除或无效,则删除当前结算数据
                if (strategyDO == null || SettlementStrategyStatusEnum.INVALID.getCode().equals(strategyDO.getStatus())) {
                    // 删除积分结算数据
                    platformScoreSettlementRepository.deleteById(o.getId());
                    // 删除积分结算明细
                    platformScoreSettlementDetailRepository.deleteAllBySettlementId(o.getId());
                } else if (CommonBooleanEnum.YES.getCode().equals(strategyDO.getIsDefault())) {
                    // 如果策略设置默认所有会员，直接重新结算
                    scoreSettlementCommon(strategyDO, DateUtil.date(o.getSettlementDate()), rate, o.getMemberId(), o.getRoleId());
                } else if (strategyDO.getMemberList() != null && strategyDO.getMemberList().size() > 0
                        && strategyDO.getMemberList().stream().anyMatch(item ->
                        item.getMemberId().equals(o.getMemberId()) || item.getRoleId().equals(o.getRoleId()))) {
                    // 如果策略设置指定会员，结算会员在策略中,直接重新结算
                    scoreSettlementCommon(strategyDO, DateUtil.date(o.getSettlementDate()), rate, o.getMemberId(), o.getRoleId());
                } else {
                    // 不在结算策略中,删除结算数据
                    // 删除积分结算数据
                    platformScoreSettlementRepository.deleteById(o.getId());
                    // 删除积分结算明细
                    platformScoreSettlementDetailRepository.deleteAllBySettlementId(o.getId());
                }
            });
        }

        return Wrapper.success();
    }

    /**
     * 平台手动结算
     * @author 伍将
     * @date 2020/12/24
     * @param settlementVO:结算参数
     * @return 统一数据模型
     **/
    @Override
    public Wrapper<Void> platformManualSettlement(UserLoginCacheDTO user, PlatformManualSettlementVO settlementVO) {
        PlatformSettlementDO settlementDO = platformSettlementRepository.findById(settlementVO.getId()).orElse(null);
        if (settlementDO == null) {
            throw new BusinessException(ResponseCode.SA_PLATFORM_SETTLEMENT_NOT_EXIST);
        }

        List<PlatformSettlementDO> settlementDOList = platformSettlementRepository.findAllByIdGreaterThanEqualAndMemberIdAndRoleIdOrderBySettlementDateAsc(settlementVO.getId(), settlementDO.getMemberId(), settlementDO.getRoleId());
        if (settlementDOList == null || settlementDOList.size() <= 0) {
            throw new BusinessException(ResponseCode.SA_PLATFORM_SETTLEMENT_NOT_EXIST);
        }
        // 如果结算数据有已对账数据，则不允许重跑结算
        if (settlementDOList.stream().anyMatch(o -> !SettlementStatusEnum.TO_BE_ACCOUNT_COMPLETE.getCode().equals(o.getStatus()))) {
            throw new BusinessException(ResponseCode.SA_SETTLEMENT_DATA_CHECK);
        }

        List<Long> idList = settlementDOList.stream().map(PlatformSettlementDO::getId).collect(Collectors.toList());

        // 获取佣金比率
        PlatformParameterManageBO platformParameterManageBO = platformParameterManageDAO.getParameter(com.ssy.lingxi.report.constant.Constants.RECEIVE_SETTLEMENT_CODE);
        Double rate = platformParameterManageBO != null ? platformParameterManageBO.getValue() : 0;

        // 重新结算前，先清除旧数据
        settlementDOList.forEach(o -> {
            o.setBrokerage(0D);
            o.setCollectAmount(0D);
            o.setTotalCount(0L);
            o.setAmount(0D);
        });
        platformSettlementRepository.saveAll(settlementDOList);
        // 删除结算明细
        platformSettlementDetailRepository.deleteAllBySettlementIdIn(idList);

        settlementDOList.forEach(o -> {
            PlatformSettlementStrategyDO strategyDO = platformSettlementStrategyRepository.findById(o.getStrategyId()).orElse(null);
            // 如果对应的结算策略已删除或无效,则删除当前结算数据
            if (strategyDO == null || SettlementStrategyStatusEnum.INVALID.getCode().equals(strategyDO.getStatus())) {
                // 删除结算数据
                platformSettlementRepository.deleteById(o.getId());
                // 删除结算明细
                platformSettlementDetailRepository.deleteAllBySettlementId(o.getId());
            } else if (CommonBooleanEnum.YES.getCode().equals(strategyDO.getIsDefault())) {
                // 如果策略设置默认所有会员，直接重新结算
                platformSettlementCommon(strategyDO, DateUtil.date(o.getSettlementDate()), rate, o.getMemberId(), o.getRoleId());
            } else if (strategyDO.getMemberList() != null && strategyDO.getMemberList().size() > 0
                    && strategyDO.getMemberList().stream().anyMatch(item ->
                    item.getMemberId().equals(o.getMemberId()) || item.getRoleId().equals(o.getRoleId()))) {
                // 如果策略设置指定会员，结算会员在策略中,直接重新结算
                platformSettlementCommon(strategyDO, DateUtil.date(o.getSettlementDate()), rate, o.getMemberId(), o.getRoleId());
            } else {
                // 不在结算策略中,删除结算数据
                // 删除结算数据
                platformSettlementRepository.deleteById(o.getId());
                // 删除结算明细
                platformSettlementDetailRepository.deleteAllBySettlementId(o.getId());
            }
        });

        return Wrapper.success();
    }

    /**
     * 根据结算日进行代收账款结算
     * @author 伍将
     * @date 2020/12/2
     * @param startDate: 结算时间
     **/
    private void platformSettlement(DateTime startDate) {

        logger.info("开始统计平台结算-代收账款");

        // 获取当前时间对应的天
        Integer day = startDate.dayOfMonth();

        PlatformParameterManageBO platformParameterManageBO = platformParameterManageDAO.getParameter(com.ssy.lingxi.report.constant.Constants.RECEIVE_SETTLEMENT_CODE);

        Double rate = platformParameterManageBO != null ? platformParameterManageBO.getValue() : 0;

        // 查询平台有效结算策略
        List<PlatformSettlementStrategyDO> strategyDOS = getSettlementStrategyList(day, PlatformSettlementOrderTypeEnum.RECEIVE.getCode());
        if (strategyDOS.size() <= 0) {
            logger.info("统计平台结算-代收账款:当前没有符合需要结算的策略");
        } else {
            // 循环处理各策略数据
            for (PlatformSettlementStrategyDO strategyDO : strategyDOS) {
                logger.info("统计平台结算-代收账款:start:{}-{}", strategyDO.getId(), strategyDO.getName());
                platformSettlementForStrategy(strategyDO, startDate, rate);
                logger.info("统计平台结算-代收账款:end:{}-{}", strategyDO.getId(), strategyDO.getName());
            }
        }

        logger.info("结束统计平台结算-代收账款");
    }

    /**
     * 根据策略进行代收账款结算
     * @author 伍将
     * @date 2020/11/13
     * @param strategyDO: 结算策略
     * @param startDate: 结算时间
     * @param rate: 佣金比率
     **/
    @Transactional
    public void platformSettlementForStrategy(PlatformSettlementStrategyDO strategyDO, DateTime startDate, Double rate) {
        if (strategyDO == null) {
            logger.info("统计平台结算-代收账款:结算策略为空");
            return;
        }
        List<PlatformSettlementStrategyMemberDO> memberDOList;
        //默认全部会员
        if(CommonBooleanEnum.YES.getCode().equals(strategyDO.getIsDefault())){
            Date settleDate=startDate.toJdkDate();
            //获得上个月的第一天
            DateTime startTime = DateUtil.beginOfMonth(DateUtil.offsetMonth(settleDate, -1));
            DateTime endTime = DateUtil.endOfDay(settleDate);
            memberDOList = procurementOrderDAO.getSettleMemberListByTime(startTime.getTime(), endTime.getTime());
        }else{//策略指定会员
            memberDOList=strategyDO.getMemberList();
        }
        if(CollectionUtil.isEmpty(memberDOList)){
            logger.info("统计平台结算-代收账款:结算策略[{}-{}]未有会员或不存在订单", strategyDO.getId(), strategyDO.getName());
            return;
        }
        for (PlatformSettlementStrategyMemberDO memberDO : memberDOList) {
            Long memberId = memberDO.getMemberId();
            Long roleId = memberDO.getRoleId();
            try {
                Wrapper<Void> wrapper = platformSettlementCommon(strategyDO, startDate, rate, memberId, roleId);
                if (wrapper == null || wrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
                    logger.info("统计平台结算-代收账款:结算策略[{}-{}-结算会员:{}-结算角色:{}],结算失败", strategyDO.getId(), strategyDO.getName(), memberId, roleId);
                }
            } catch (Exception e) {
                logger.error("统计平台结算-代收账款:结算策略[{}-{}-结算会员:{}-结算角色:{}],结算失败", strategyDO.getId(), strategyDO.getName(), memberId, roleId,e);
            }
        }
    }

    /**
     * 平台结算通用方法
     * @author 伍将
     * @date 2020/12/24
     * @param strategyDO: 结算策略
     * @param startDate: 结算时间
     * @param rate: 佣金比率
     * @param memberId: 结算会员id
     * @param roleId: 结算角色id
     * @return 统一数据模型
     **/
    @Transactional
    public Wrapper<Void> platformSettlementCommon(PlatformSettlementStrategyDO strategyDO, DateTime startDate, Double rate, Long memberId, Long roleId) {
        Long settlementDate = DateUtil.parseDate(startDate.toString("yyyy-MM-dd")).getTime();

        // 查询是否存在结算数据
        PlatformSettlementDO platformSettlementDO = platformSettlementRepository.getDetail(settlementDate, memberId, roleId, strategyDO.getSettlementWay());
        if (platformSettlementDO != null) {
            if (!platformSettlementDO.getStatus().equals(SettlementStatusEnum.TO_BE_ACCOUNT_COMPLETE.getCode())) {
                logger.info("统计平台结算-代收账款:结算策略[{}-{}-结算会员:{}-结算角色:{},已结算", strategyDO.getId(), strategyDO.getName(), memberId, roleId);
                throw new BusinessException(ResponseCode.SA_SETTLEMENT_DATA_CHECK);
            }

            // 清除原始数据
            platformSettlementDO.setTotalCount(0L);
            platformSettlementDO.setCollectAmount(0D);
            platformSettlementDO.setBrokerage(0D);
            platformSettlementDO.setAmount(0D);
            platformSettlementDO.setSettlementTime(System.currentTimeMillis());
            platformSettlementRepository.saveAndFlush(platformSettlementDO);

            // 删除代收账款结算明细
            platformSettlementDetailRepository.deleteAllBySettlementId(platformSettlementDO.getId());
        } else {

            platformSettlementDO = new PlatformSettlementDO();
            platformSettlementDO.setMemberId(memberId);
            platformSettlementDO.setRoleId(roleId);
            platformSettlementDO.setSettlementName("-");
            platformSettlementDO.setTotalCount(0L);
            platformSettlementDO.setCollectAmount(0D);
            platformSettlementDO.setAmount(0D);
            platformSettlementDO.setBrokerage(0D);

            // 获取会员名称
            List<Long> memberIds = new ArrayList<>();
            memberIds.add(memberId);
            List<MemberBO> memberBOS = memberDAO.getMemberList(memberIds);
            if (memberBOS != null && memberBOS.size() > 0) {
                MemberBO memberBO = memberBOS.stream().filter(o -> o.getId().equals(memberId)).findFirst().orElse(null);
                platformSettlementDO.setSettlementName(memberBO != null ? memberBO.getName() : "-");
            }

            //交易订单号
            String settlementNo = SerialNumberUtils.getNumber(redisStringUtils, Constants.SETTLEMENT_CODE, Constants.SETTLEMENT_CODE_DATE, Constants.SETTLEMENT_CODE_NUM_LEN);
            platformSettlementDO.setSettlementNo(settlementNo);
            platformSettlementDO.setSettlementDate(settlementDate);
            platformSettlementDO.setSettlementWay(strategyDO.getSettlementWay());
            platformSettlementDO.setSettlementTime(System.currentTimeMillis());
            platformSettlementDO.setStatus(SettlementStatusEnum.TO_BE_ACCOUNT_COMPLETE.getCode());
            platformSettlementDO.setPayWay(strategyDO.getSettlementPaymentType());
            platformSettlementDO.setStrategyId(strategyDO.getId());
            platformSettlementDO.setUpdateTime(0L);
        }

        long endTime;
        if (strategyDO.getSettlementWay().equals(SettlementWayEnum.ACCOUNT_PERIOD.getCode())) {
            // 账期结算截止时间:T+账期天数
            endTime = DateUtil.endOfDay(DateUtil.offsetDay(startDate,-strategyDO.getSettlementDays())).getTime();
        } else if (strategyDO.getSettlementWay().equals(SettlementWayEnum.MONTH_SETTLEMENT.getCode())) {
            // 月结结算截止时间:上个月最后一天时间
            endTime = DateUtil.endOfMonth(DateUtil.offsetMonth(startDate, -1)).getTime();
        } else {
            logger.info("统计平台结算-生成通知单:结算策略[{}-{}]结算方式有误", strategyDO.getId(), strategyDO.getName());
            throw new BusinessException(ResponseCode.SA_SETTLEMENT_WAY_ERROR);
        }

        // 获取最后一次代收账款结算
        PlatformSettlementDetailDO settlementDetailDO = platformSettlementDetailRepository.findFirstByMemberIdAndRoleIdOrderByPayTimeDesc(memberId, roleId);
        //在原有时间上加1，防止后续查询数据重复
        Long startTime = (settlementDetailDO != null ? (settlementDetailDO.getPayTime() + 1000) : 0);

        // 设置预计付款日期
        LocalDateTime settlementDatetime = Instant.ofEpochMilli(settlementDate).atZone(ZoneId.systemDefault()).toLocalDateTime();
        long estimatedPaymentDate = Optional.ofNullable(strategyDO.getEstimatedPaymentDate()).map(map -> {
            LocalDateTime estimatedPaymentDatetime = settlementDatetime.withDayOfMonth(map);
            return estimatedPaymentDatetime.compareTo(settlementDatetime) >= 0
                    ? estimatedPaymentDatetime.toLocalDate().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()
                    : estimatedPaymentDatetime.plusMonths(1).toLocalDate().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
        }).orElse(settlementDate);
        // 设置结算时间范围
        platformSettlementDO.setEstimatedPaymentDate(estimatedPaymentDate);
        platformSettlementDO.setStartTime(startTime);
        platformSettlementDO.setEndTime(endTime);

        // 处理订单数据
        //PlatformSettlementBO platformSettlementBO = procurementOrderDAO.getSettlement(platformSettlementDO.getStartTime(), platformSettlementDO.getEndTime(), memberId, roleId);
        PlatformSettlementBO platformSettlementBO = procurementOrderDAO.getSettlementBak(strategyDO.getSettlementPaymentType(),platformSettlementDO.getStartTime(), platformSettlementDO.getEndTime(), memberId, roleId);
        if (platformSettlementBO != null) {
            // 保存代收账款结算
            platformSettlementDO.setTotalCount(platformSettlementBO.getTotalCount());
            platformSettlementDO.setCollectAmount(platformSettlementBO.getTotalAmount());
            platformSettlementDO.setBrokerage(platformSettlementBO.getTotalAmount() * rate / 100);
            platformSettlementDO.setAmount(platformSettlementDO.getCollectAmount() - platformSettlementDO.getBrokerage());
            platformSettlementRepository.saveAndFlush(platformSettlementDO);
            Long settlementId = platformSettlementDO.getId();

            // 处理代收账款结算明细
            // List<PlatformSettlementDetailCVO> detailList = procurementOrderDAO.getSettlementDetail(platformSettlementDO.getStartTime(), platformSettlementDO.getEndTime(), memberId, roleId);
            List<PlatformSettlementDetailCVO> detailList = procurementOrderDAO.getSettlementDetailBak(strategyDO.getSettlementPaymentType(), platformSettlementDO.getStartTime(), platformSettlementDO.getEndTime(), memberId, roleId);
            if (detailList != null && detailList.size() > 0) {
                List<PlatformSettlementDetailDO> detailDOS=new ArrayList<>();
                detailList.forEach(cvo -> {
                    PlatformSettlementDetailDO o=new PlatformSettlementDetailDO();
                    BeanUtils.copyProperties(cvo,o);
                    o.setSettlementId(settlementId);
                    o.setSettlementOrderType(SettlementOrderTypeEnum.ORDER.getCode());
                    o.setMemberId(memberId);
                    o.setRoleId(roleId);
                    o.setBrokerage(o.getCollectAmount() * rate / 100);
                    o.setSettlementAmount(o.getCollectAmount() - o.getBrokerage());
                    o.setRatio(rate);
                    o.setOrderAbstract(null != o.getOrderAbstract() ? o.getOrderAbstract() : "-");
                    o.setPaymentId(cvo.getPaymentId());
                    o.setPaymentTradeNo(cvo.getPaymentTradeNo());
                    detailDOS.add(o);
                });
                platformSettlementDetailRepository.saveAll(detailDOS);
            }
        }

        // 处理售后退款数据
//        platformSettlementBO = returnGoodsDAO.getSettlement(platformSettlementDO.getStartTime(), platformSettlementDO.getEndTime(), memberId, roleId);
        platformSettlementBO = returnGoodsDAO.getSettlementBak(strategyDO.getSettlementPaymentType(),platformSettlementDO.getStartTime(), platformSettlementDO.getEndTime(), memberId, roleId);
        if (platformSettlementBO != null) {
            // 保存代收账款结算
            platformSettlementDO.setTotalCount(platformSettlementDO.getTotalCount() + platformSettlementBO.getTotalCount());
            platformSettlementDO.setCollectAmount(platformSettlementDO.getCollectAmount() - platformSettlementBO.getTotalAmount());
            //退款没有佣金
            platformSettlementDO.setAmount(platformSettlementDO.getAmount() - platformSettlementBO.getTotalAmount());
            platformSettlementRepository.saveAndFlush(platformSettlementDO);
            Long settlementId = platformSettlementDO.getId();

            // 处理代收账款结算明细
            // List<PlatformSettlementDetailDO> detailList = returnGoodsDAO.getSettlementDetail(platformSettlementDO.getStartTime(), platformSettlementDO.getEndTime(), memberId, roleId);
            List<PlatformSettlementDetailDO> detailList = returnGoodsDAO.getSettlementDetailBak(strategyDO.getSettlementPaymentType(),platformSettlementDO.getStartTime(), platformSettlementDO.getEndTime(), memberId, roleId);
            if (detailList != null && detailList.size() > 0) {
                detailList.forEach(o -> {
                    o.setSettlementId(settlementId);
                    o.setOrderType(999);    // 退货
                    o.setSettlementOrderType(SettlementOrderTypeEnum.RETURN_GOODS.getCode());
                    o.setMemberId(memberId);
                    o.setRoleId(roleId);
                    o.setBrokerage(0D); //退款没有佣金
                    o.setRatio(0D);
                    o.setCollectAmount(-o.getCollectAmount());
                    o.setSettlementAmount(o.getCollectAmount());
                    o.setOrderAbstract(null != o.getOrderAbstract() ? o.getOrderAbstract() : "-");
                });
                platformSettlementDetailRepository.saveAll(detailList);
            }
        }

        // 新增系统消息通知
        SystemMessageRequest request = new SystemMessageRequest();
        request.setMemberId(platformSettlementDO.getMemberId());
        request.setRoleId(platformSettlementDO.getRoleId());
        request.setMessageNotice(MessageTemplateCode.agency_receipt_check);
        List<String> params = new ArrayList<>();
        params.add(platformSettlementDO.getSettlementNo());
        request.setParams(params);
        feignService.sendSystemMessage(request);

        // 如果新增了结算数据表，且结算订单小于等于0，则说明此次没有数据需要结算，删除平台结算数据
        if (platformSettlementDO.getId() != null && platformSettlementDO.getId() > 0
                && platformSettlementDO.getTotalCount() <= 0) {
            platformSettlementRepository.delete(platformSettlementDO);
        }

        return Wrapper.success();
    }

    /**
     * 根据结算日进行积分结算
     * @author 伍将
     * @date 2020/12/2
     * @param startDate: 结算时间
     **/
    private void scoreSettlement(DateTime startDate) {

        logger.info("开始统计平台结算-积分");

        // 获取当前时间对应的天
        Integer day = startDate.dayOfMonth();

        PlatformParameterManageBO platformParameterManageBO = platformParameterManageDAO.getParameter(com.ssy.lingxi.report.constant.Constants.SCORE_SETTLEMENT_EXCHANGE_CODE);
        if (platformParameterManageBO == null) {
            logger.info("平台未配置有效的积分兑换比率");
            return;
        } else if (platformParameterManageBO.getValue() <= 0) {
            logger.info("平台配置有效的积分兑换比率小于等于0，无需结算");
            return;
        }

        // 查询积分有效结算策略
        List<PlatformSettlementStrategyDO> strategyDOS = getSettlementStrategyList(day, PlatformSettlementOrderTypeEnum.SCORE.getCode());
        if (strategyDOS.size() <= 0) {
            logger.info("统计平台结算-积分:当前没有符合需要结算的策略");
        } else {
            // 循环处理各策略数据
            for (PlatformSettlementStrategyDO strategyDO : strategyDOS) {
                logger.info("统计平台结算-积分:start:{}-{}", strategyDO.getId(), strategyDO.getName());
                scoreSettlementForStrategy(strategyDO, startDate, platformParameterManageBO.getValue());
                logger.info("统计平台结算-积分:end:{}-{}", strategyDO.getId(), strategyDO.getName());
            }
        }

        logger.info("结束统计平台结算-积分");
    }

    /**
     * 根据策略进行积分结算
     * @author 伍将
     * @date 2020/11/13
     * @param strategyDO: 结算策略
     * @param startDate: 结算时间
     * @param rate： 兑换比率
     **/
    @Transactional
    public void scoreSettlementForStrategy(PlatformSettlementStrategyDO strategyDO, DateTime startDate, Double rate) {
        if (strategyDO == null) {
            logger.info("统计平台结算-积分:结算策略为空");
            return;
        }
        List<PlatformSettlementStrategyMemberDO> memberDOList;
        // 默认全部会员
        if (CommonBooleanEnum.YES.getCode().equals(strategyDO.getIsDefault())) {
            Date settleDate=startDate.toJdkDate();
            //获得上个月的第一天
            DateTime startTime = DateUtil.beginOfMonth(DateUtil.offsetMonth(settleDate, -1));
            DateTime endTime = DateUtil.endOfDay(settleDate);
            memberDOList = procurementOrderDAO.getScoreSettleMemberListByTime(startTime.getTime(), endTime.getTime());
        } else { // 策略指定会员
            memberDOList=strategyDO.getMemberList();
        }

        if (CollectionUtil.isEmpty(memberDOList)) {
            logger.info("统计平台结算-积分:结算策略[{}-{}]未有会员或不存在订单", strategyDO.getId(), strategyDO.getName());
            return;
        }

        for (PlatformSettlementStrategyMemberDO memberDO : memberDOList) {
            Long memberId = memberDO.getMemberId();
            Long roleId = memberDO.getRoleId();
            try {
                Wrapper<Void> wrapper = scoreSettlementCommon(strategyDO, startDate, rate, memberId, roleId);
                if (wrapper == null || wrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
                    logger.info("统计平台结算-积分:结算策略[{}-{}-结算会员:{}-结算角色:{}],结算失败", strategyDO.getId(), strategyDO.getName(), memberId, roleId);
                }
            } catch (Exception e) {
                logger.info("统计平台结算-积分:结算策略[{}-{}-结算会员:{}-结算角色:{}],结算失败", strategyDO.getId(), strategyDO.getName(), memberId, roleId);
            }
        }
    }

    /**
     * 平台积分结算通用方法
     * @author 伍将
     * @date 2020/12/24
     * @param strategyDO: 结算策略
     * @param startDate: 结算时间
     * @param rate: 兑换比率
     * @param memberId: 结算会员id
     * @param roleId: 结算角色id
     * @return 统一数据模型
     **/
    @Transactional
    public Wrapper<Void> scoreSettlementCommon(PlatformSettlementStrategyDO strategyDO, DateTime startDate, Double rate, Long memberId, Long roleId) {

        Long settlementDate = DateUtil.parseDate(startDate.toString("yyyy-MM-dd")).getTime();

        // 查询是否存在结算数据
        PlatformScoreSettlementDO platformScoreSettlementDO = platformScoreSettlementRepository.getDetail(settlementDate, memberId, roleId, strategyDO.getSettlementWay());
        if (platformScoreSettlementDO != null) {
            if (!platformScoreSettlementDO.getStatus().equals(SettlementStatusEnum.TO_BE_ACCOUNT_COMPLETE.getCode())) {
                logger.info("统计平台结算-积分:结算策略[{}-{}-结算会员:{}-结算角色:{},已结算", strategyDO.getId(), strategyDO.getName(), memberId, roleId);
                throw new BusinessException(ResponseCode.SA_SETTLEMENT_DATA_CHECK);
            }

            // 清除原始数据
            platformScoreSettlementDO.setTotalCount(0L);
            platformScoreSettlementDO.setScore(0D);
            platformScoreSettlementDO.setRatio(0D);
            platformScoreSettlementDO.setAmount(0D);
            platformScoreSettlementDO.setSettlementTime(System.currentTimeMillis());
            platformScoreSettlementRepository.saveAndFlush(platformScoreSettlementDO);

            // 删除积分结算明细
            platformScoreSettlementDetailRepository.deleteAllBySettlementId(platformScoreSettlementDO.getId());
        } else {

            platformScoreSettlementDO = new PlatformScoreSettlementDO();
            platformScoreSettlementDO.setMemberId(memberId);
            platformScoreSettlementDO.setRoleId(roleId);
            platformScoreSettlementDO.setSettlementName("-");

            // 获取会员名称
            List<Long> memberIds = new ArrayList<>();
            memberIds.add(memberId);
            List<MemberBO> memberBOS = memberDAO.getMemberList(memberIds);
            if (memberBOS != null && memberBOS.size() > 0) {
                MemberBO memberBO = memberBOS.stream().filter(o -> o.getId().equals(memberId)).findFirst().orElse(null);
                platformScoreSettlementDO.setSettlementName(memberBO != null ? memberBO.getName() : "-");
            }

            //交易订单号
            String settlementNo = SerialNumberUtils.getNumber(redisStringUtils, Constants.SETTLEMENT_CODE, Constants.SETTLEMENT_CODE_DATE, Constants.SETTLEMENT_CODE_NUM_LEN);
            platformScoreSettlementDO.setSettlementNo(settlementNo);
            platformScoreSettlementDO.setSettlementDate(settlementDate);
            platformScoreSettlementDO.setSettlementWay(strategyDO.getSettlementWay());
            platformScoreSettlementDO.setSettlementTime(System.currentTimeMillis());
            platformScoreSettlementDO.setStatus(SettlementStatusEnum.TO_BE_ACCOUNT_COMPLETE.getCode());
            platformScoreSettlementDO.setPayWay(strategyDO.getSettlementPaymentType());
            platformScoreSettlementDO.setStrategyId(strategyDO.getId());
            platformScoreSettlementDO.setUpdateTime(0L);
        }

        long endTime;
        if (SettlementWayEnum.ACCOUNT_PERIOD.getCode().equals(strategyDO.getSettlementWay())) {
            // 账期结算截止时间:T+账期天数
            endTime = DateUtil.endOfDay(DateUtil.offsetDay(startDate,-strategyDO.getSettlementDays())).getTime();
        } else if (SettlementWayEnum.MONTH_SETTLEMENT.getCode().equals(strategyDO.getSettlementWay())) {
            // 月结结算截止时间:上个月最后一天时间
            endTime = DateUtil.endOfMonth(DateUtil.offsetMonth(startDate, -1)).getTime();
        } else {
            logger.info("统计平台结算-生成通知单:结算策略[{}-{}]结算方式有误", strategyDO.getId(), strategyDO.getName());
            throw new BusinessException(ResponseCode.SA_SETTLEMENT_WAY_ERROR);
        }

        // 获取最后一次积分结算
        PlatformScoreSettlementDetailDO scoreSettlementDetailDO = platformScoreSettlementDetailRepository.findFirstByMemberIdAndRoleIdOrderByPayTimeDesc(memberId, roleId);
        //在原有时间上加1，防止后续查询数据重复
        Long startTime = (scoreSettlementDetailDO != null ? (scoreSettlementDetailDO.getPayTime()+ 1000) : 0);

        // 预计付款日期
        LocalDateTime settlementDatetime = Instant.ofEpochMilli(settlementDate).atZone(ZoneId.systemDefault()).toLocalDateTime();
        long estimatedPaymentDate = Optional.ofNullable(strategyDO.getEstimatedPaymentDate()).map(map -> {
            LocalDateTime estimatedPaymentDatetime = settlementDatetime.withDayOfMonth(map);
            return estimatedPaymentDatetime.compareTo(settlementDatetime) >= 0
                    ? estimatedPaymentDatetime.toLocalDate().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()
                    : estimatedPaymentDatetime.plusMonths(1).toLocalDate().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
        }).orElse(settlementDate);
        platformScoreSettlementDO.setEstimatedPaymentDate(estimatedPaymentDate);
        // 设置结算时间范围
        platformScoreSettlementDO.setStartTime(startTime);
        platformScoreSettlementDO.setEndTime(endTime);

        PlatformScoreSettlementBO platformScoreSettlementBO = procurementOrderDAO.getScoreSettlement(platformScoreSettlementDO.getStartTime(), platformScoreSettlementDO.getEndTime(), memberId, roleId);
        if (platformScoreSettlementBO != null) {
            // 保存积分结算
            platformScoreSettlementDO.setTotalCount(platformScoreSettlementBO.getTotalCount());
            platformScoreSettlementDO.setScore(platformScoreSettlementBO.getTotalScore());
            platformScoreSettlementDO.setRatio(rate);
            platformScoreSettlementDO.setAmount(platformScoreSettlementBO.getTotalScore() * rate / 100);
            platformScoreSettlementRepository.saveAndFlush(platformScoreSettlementDO);
            Long settlementId = platformScoreSettlementDO.getId();

            // 处理积分结算明细
            List<PlatformScoreSettlementDetailCVO> detailList = procurementOrderDAO.getScoreSettlementDetail(platformScoreSettlementDO.getStartTime(), platformScoreSettlementDO.getEndTime(), memberId, roleId);
            if (detailList != null && detailList.size() > 0) {
                List<PlatformScoreSettlementDetailDO> detailDOS=new ArrayList<>();
                detailList.forEach(cvo -> {
                    PlatformScoreSettlementDetailDO o=new PlatformScoreSettlementDetailDO();
                    BeanUtils.copyProperties(cvo,o);
                    o.setSettlementId(settlementId);
                    o.setSettlementOrderType(SettlementOrderTypeEnum.ORDER.getCode());
                    o.setMemberId(memberId);
                    o.setRoleId(roleId);
                    o.setSettlementAmount(NumberUtil.div(NumberUtil.mul(o.getPayScore(), rate),100));
                    o.setRatio(rate);
                    o.setOrderAbstract(null != o.getOrderAbstract() ? o.getOrderAbstract() : "-");
                    // 积分抵扣重新设置兑换比例
                    if (!Objects.equals(cvo.getOrderType(), 7) && !Objects.equals(cvo.getOrderType(), 8)) {
                        o.setSettlementAmount(cvo.getSettlementAmount());
                        double ratio = NumberUtil.mul(NumberUtil.div(o.getSettlementAmount(), o.getPayScore()), 100);
                        o.setRatio(ratio);
                    }
                    detailDOS.add(o);
                });
                platformScoreSettlementDetailRepository.saveAll(detailDOS);
                // 更新结算金额
                Double amount = detailDOS.stream().map(PlatformScoreSettlementDetailDO::getSettlementAmount).reduce(NumberUtil::add).map(map -> BigDecimal.valueOf(map).setScale(2, RoundingMode.HALF_UP).doubleValue()).orElse(0D);
                platformScoreSettlementDO.setAmount(amount);
                platformScoreSettlementRepository.saveAndFlush(platformScoreSettlementDO);

            }

            // 新增系统消息通知
            SystemMessageRequest request = new SystemMessageRequest();
            request.setMemberId(platformScoreSettlementDO.getMemberId());
            request.setRoleId(platformScoreSettlementDO.getRoleId());
            request.setMessageNotice(MessageTemplateCode.integral_check);
            List<String> params = new ArrayList<>();
            params.add(platformScoreSettlementDO.getSettlementNo());
            request.setParams(params);
            feignService.sendSystemMessage(request);
        } else {

            // 如果新增了结算数据表，且结算订单小于等于0，则说明此次没有数据需要结算，删除平台结算数据
            if (platformScoreSettlementDO.getId() != null && platformScoreSettlementDO.getId() > 0
                    && platformScoreSettlementDO.getTotalCount() <= 0) {
                platformScoreSettlementRepository.delete(platformScoreSettlementDO);
            }

            logger.info("统计平台结算-积分:结算策略[{}-{}-结算会员:{}-结算角色:{}],无需结算", strategyDO.getId(), strategyDO.getName(), memberId, roleId);
        }

        return Wrapper.success();
    }

    /**
     * 获取有效结算策略
     * @author 伍将
     * @date 2020/12/14
     * @param day: 结算日
     * @param settlementOrderType:结算单据类型
     * @return 结算策略集合
     **/
    private List<PlatformSettlementStrategyDO> getSettlementStrategyList(Integer day, Integer settlementOrderType) {
        Specification<PlatformSettlementStrategyDO> spec =  (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(criteriaBuilder.equal(root.get("status").as(Integer.class), SettlementStrategyStatusEnum.VALID.getCode())); // 查询有效
            list.add(criteriaBuilder.equal(root.get("settlementOrderType").as(Integer.class), settlementOrderType)); // 结算单据方式
            list.add(
                    criteriaBuilder.or(
                            criteriaBuilder.equal(root.get("settlementWay").as(Integer.class), SettlementWayEnum.ACCOUNT_PERIOD.getCode()),
                            criteriaBuilder.and(
                                    criteriaBuilder.equal(root.get("settlementWay").as(Integer.class), SettlementWayEnum.MONTH_SETTLEMENT.getCode()),
                                    criteriaBuilder.equal(root.get("settlementDate").as(Integer.class), day)
                            )
                    )
            );

            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };

        return platformSettlementStrategyRepository.findAll(spec);
    }
}
